package commander;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;


import branch.Branch;
import core.*;
import gitobject.Commit;
import gitobject.GitObject;
import repository.Repository;
import stage.Index;


public class CLI {	
	
	/**
	 * Command 'jit init'
	 * @param args
	 * @throws IOException
	 */
	public static void jitInit(String[] args) throws IOException {
		String path = "";
		if(args.length <= 2) { //get default working path
			path = new File(".").getCanonicalPath();
			JitInit.init(path);
		}else if(args[2].equals("-help")){ //see help
			System.out.println("usage: jit init [<path>] [-help]\r\n" +
					"\r\n" +
					"jit init [<path>]:	Create an empty jit repository or reinitialize an existing one in the path or your default working directory.");
		}else {
			path = args[2];
			if(!new File(path).isDirectory()) { //if the working path input is illegal
				System.out.println(path + "is not a legal directory. Please init your reposiroty again. See 'jit init -help'.");
			}else {
				JitInit.init(path);
			}
		}
	}

	/**
	 * Command 'jit add'
	 * @param args
	 * @throws IOException
	 */
	public static void jitAdd(String[] args) throws IOException {

		String workDir = Repository.getWorkTree();

		if (args.length <= 2 || (args.length > 2 && args[2].equals("-help"))) {
			System.out.println("usage: jit add <file1> [<file2>...] [-help]\r\n" +
					"\r\n" + "jit add <file1> [<file2>...]: Add file(s) to stage.");
		} else if(args[2].equals(".")){ // jit add .
			File file = new File(workDir);
			try{
				JitAdd.add(file);
			}catch(Exception e){
				e.printStackTrace();
			}
		} else{
			for (int i = 2; i < args.length; i++) {
				String fileName = args[i];
				File file = new File(workDir + File.separator + fileName);
				try {
					JitAdd.add(file);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}



	/**
	 * Command 'jit rm'
	 * @param args
	 * @throws IOException
	 */
	public static void jitRM(String[] args) throws IOException {
		if(args.length <= 2) { // if the input is illegal
			System.out.println("fatal: No pathspec was given. Which files should I remove?\n");
		}else if(args[2].equals("-help")){ //if want to read help doc
			System.out.println("$ jit rm -help\n" +
					"usage: jit rm [<options>] [--] <file>...\n" +
					"\n" +
					"    [default]             remove from index and workspace (consider file changes)\n" +
					"    --cached              remove from index\n" +
					"    -f                    remove from index and workspace (ignore file changes)\n");
		}else if(args.length == 3) {
			JitRM.rmDefault(args[2]); // default pattern: remove from index and workspace (consider file changes)
		}else if(args.length == 4 && args[2].equals("--cached")){
			JitRM.rmCached(args[3]); // --cached pattern: remove from index
		}else if(args.length == 4 && args[2].equals("-f")){
			JitRM.rmF(args[3]); // -f pattern: remove from index and workspace (ignore file changes)
		} else{ //Some features that do not exist or are not yet supported
			System.out.println("Jit can not recognize your choice. Some git rm operations will be supported later. Please read jit rm-help.");
		}
	}

	/**
	 * Command 'jit commit'
	 * @param args
	 * @throws Exception
	 */
	public static void jitCommit(String[] args) throws Exception {
		if(args.length == 2) {
			System.out.println("Error: commit message is necessary.");
			System.out.println("Please enter the commit message: ");
			Scanner sc = new Scanner(System.in);
			String message = sc.nextLine();
			JitCommit jc = new JitCommit();
			jc.commit(message);
		}else if(args[2].equals("-help")){
			System.out.println("usage: jit commit [-help]\r\n" +
					"\r\n" +
					"jit commit: Commit file(s) from stage to repository.");
		}
		else if(args[2].equals("-m")){
			JitCommit jc = new JitCommit();
			jc.commit(args[3]);
		}
	}

	/**
	 * Command 'jit log'
	 * @param args
	 * @throws IOException
	 */
	public static void jitLog(String[] args) throws Exception {
		if(args.length == 2){
			JitLog.log(Repository.getWorkTree());
		}else if(args[2].equals("-help")){
			System.out.println("usage: git log");
		}else{
			System.out.println("Jit can not recognize your choice. Some jit log operations will be supported later. Please read jit log -help.");
		}
	}
	private static void jitCatFile(String[] args) throws UnsupportedEncodingException {
		if(args.length == 4){
			if(!args[3].matches("[a-z0-9]{4,}")){
				System.out.printf("fatal: Not a valid object name %s\n", args[3]);
			}else if(args[2].equals("-t")){
				JitCatFile.cat(args[3],"type");
			}else if(args[2].equals("-p")){
				JitCatFile.cat(args[3],"content");
			}else if(args[2].equals("-s")){
				JitCatFile.cat(args[3],"length");
			}
		}else{
			System.out.println("usage: git cat-file (-t | -p | <object>\n");
		}
	}
	private static void jitListIndex(String[] args) throws IOException {
		if(args.length == 2){
			JitLsFile.lsSimple();
		}
		if(args.length == 3){
			if(args[2].equals("-s")){
				JitLsFile.lsLong();
			}
		}
	}

	public static void jitReset(String[] args) throws IOException {
		//match hash or head^ or head~n
		String pattern = "^(?i)head(?:\\^*|~\\d*)|[a-z1-9]{4,}";
		if(args.length == 2){// jit reset
			JitReset.mixedReset("HEAD");
		}else{
			if(args[2].equals("-help")){
				System.out.println("usage: git reset [--mixed | --soft | --hard] [<commit>]" );
			}else if(args[2].equals("--soft")) {
				try{
					JitReset.softReset(args[3]);
				}catch(IndexOutOfBoundsException e){
					JitReset.softReset("HEAD");
				}catch(IOException e){
					e.printStackTrace();
				}
			}else if(args[2].equals("--hard")){
				try{
					JitReset.hardReset(args[3]);
				}catch(IndexOutOfBoundsException e){
					JitReset.hardReset("HEAD");
				}catch(FileNotFoundException e) {
					e.printStackTrace();
				}catch (IOException e) {
					e.printStackTrace();
				}
			}else if(args[2].equals("--mixed") ){
				try{
					JitReset.mixedReset(args[3]);
				}catch(IndexOutOfBoundsException e){
					JitReset.mixedReset("HEAD");
				}catch (IOException e){
					e.printStackTrace();
				}
			}else if(Pattern.matches(pattern, args[2])){
				JitReset.mixedReset(args[2]);
			}else{
				System.out.printf("fatal: ambiguous argument %s: unknown revision or path not in the working tree.\n" , args[2]);
			}
		}
	}


	private static void jitStatus(String[] args) throws Exception {
		if(args.length == 2){
			JitStatus.getStatus();
		}else if(args[2].equals("-help")){
			System.out.println("usage: git status [<options>] [--] <pathspec>" );
		}
	}

	/**
	 * command 'jit branch'
	 * @param args
	 * @throws Exception
	 */
	public static void jitBranch(String[] args) throws IOException {
		if (args.length < 2 || (args.length > 2 && args[2].equals("-help"))) { //'jit branch -help'
			System.out.println("usage: jit branch [branch-name] [-d branch-name] [-help]\r\n" +
					"\r\n" +
					"jit branch: List all local branches.\r\n" +
					"\r\n" +
					"jit branch [branch-name]: Create the branch.\r\n" +
					"\r\n" +
					"jit branch [branch-name] [commit]: Create the branch and point it to the commit.\r\n" +
					"\r\n" +
					"jit branch -d [branch-name]: Delete the branch.");
		} else if (args.length == 2) { //'jit branch'
			JitBranch.listBranch();
		} else if (args.length == 3) { //'jit branch [branch-name]'
			String branchName = args[2];
			JitBranch.branch(branchName);
		} else if (args.length == 4 && args[2].equals("-d")) { //'jit branch -d [branch-name]'
			String branchName = args[3];
			JitBranch.deleteBranch(branchName);
		} else if (args.length == 4) { //'jit branch [branch] [commit]'
			String branchName = args[2];
			String commitId = args[3];
			JitBranch.branch(branchName, commitId);
		}
	}
	/**
	 * command 'jit checkout'
	 * @param args
	 * @throws IOException
	 */
	public static void jitCheckout(String[] args) throws IOException {
		if (args.length <= 2 || (args.length > 2 && args[2].equals("-help"))) { //'jit checkout -help'
			System.out.println("usage: jit checkout [branch-name] [-b branch-name] [-help]\r\n" +
					"\r\n" + "jit checkout [branch]: Switch to the branch.\r\n" );
		} else if (args.length == 3) { //'jit checkout [branch]'
			String branchName = args[2];
			JitCheckout.checkout(branchName);
			System.out.println("\nSwitched to " + branchName);
		}
		else if (args.length == 4 && args[2].equals("-b")){
			String branchName = args[3];
			JitCheckout.createCheckout(branchName);
			System.out.println("\nCreated and switched to " + branchName);
		}
	}

	/**
	 * command 'jit merge'
	 * @param args
	 * @throws IOException
	 */
	public static void jitMerge(String[] args) throws Exception {
		if(args.length <= 2 || args[2].equals("-help")){
			System.out.println("usage: git merge [<options>] [<commit>...]");
		}else if(args.length == 3){
			JitMerge.merge(args[2]);
		}else if(args.length == 4){
			//JitMerge.merge(args[3], args[4]);
		}
	}

	/**
	 * command 'jit diff'
 	 * @param args
	 * @throws Exception
	 */
	public static void jitDiff(String args[]) throws Exception {
//		JitDiff.compareCommit("8a3f167d3fb11ae1e3add3ed054b8a2c4fa92eb0","daef31dd88a2581bd8b83c052578dc910b361d4b");
		if(args.length < 3 || (args.length >= 3 && args[2].equals("-help"))) {
			System.out.println("usage: jit diff [--cached] [branch-name1] [branch-name2] [-help]\r\n" +
					"\r\n" +
					"jit diff --cached: Show the difference between stage and repository.\r\n" +
					"\r\n" +
					"jit diff [first-branch] [second-branch]: Show the difference between two branches." +
					"jit diff -f [first file] [second file]: Show the difference between two files.");
		}else if(args[2].equals("--cached")) { //'jit diff --cached'
			JitDiff.compareCache();
		}else if(args.length == 4 ) { //'jit diff [first branch] [second branch]'
			String branch1Name = args[2];
			String branch2Name = args[3];
			Branch branch1 = new Branch(branch1Name);
			Branch branch2 = new Branch(branch2Name);
			String cmt1 = branch1.commitID;
			String cmt2 = branch2.commitID;
			JitDiff.compareCommit(cmt1, cmt2);
		}
		else if(args.length == 5 && args[2].equals("-f")) { //'jit diff -f [first file] [second file]'
			String file1Name = args[3];
			String file2Name = args[4];
			File file1 = new File(file1Name);
			File file2 = new File(file2Name);
			JitDiff.compareFile(file1,file2);
			JitDiff.fileChange(file1,file2);
		}else if(args.length == 5 && args[2].equals("-c")){//'jit diff -c [first commit] [second commit]'
			String commit1ID = args[3];
			String commit2ID = args[4];
			JitDiff.compareCommit(commit1ID,commit2ID);
		}
	}

	/**
	 * Command 'jit help'.
	 */
	public static void jitHelp() {
		System.out.println("usage: jit [--version] [--help] [-C <path>] [-c name=value]\r\n" +
				"           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\r\n" +
				"           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\r\n" +
				"           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\r\n" +
				"           <command> [<args>]\r\n" +
				"\r\n" +
				"These are common Jit commands used in various situations:\r\n" +
				"\r\n" +
				"start a working area\r\n" +
				"   init       Create an empty Jit repository or reinitialize an existing one\r\n" +
				"\r\n" +
				"work on the current change\r\n" +
				"   add        Add file contents to the index\r\n" +
				"   reset      Reset current HEAD to the specified state\r\n" +
				"   rm         Remove files from the working tree and from the index\r\n" +
				"\r\n" +
				"examine the history and state\r\n" +
				"   log        Show commit logs\r\n" +
				"   status     Show the working tree status\r\n" +
				"\r\n" +
				"grow, mark and tweak your common history\r\n" +
				"   branch     List, create, or delete branches\r\n" +
				"   checkout   Switch branches or restore working tree files\r\n" +
				"   commit     Record changes to the repository\r\n" +
				"   diff       Show changes between commits, commit and working tree, etc\r\n" +
				"   merge      Join two or more development histories together\r\n" +
				"\r\n" +
				"'jit help -a' and 'jit help -g' list available subcommands and some\r\n" +
				"concept guides. See 'jit help <command>' or 'jit help <concept>'\r\n" +
				"to read about a specific subcommand or concept.");
	}

	public static void main(String[] args) throws Exception {
		String path = new File(".").getCanonicalPath();
		Repository repo = new Repository(path);
		if(args.length <= 1 || args[1].equals("help")) {
			jitHelp();
		}else {
			if(args[1].equals("init")) {
				jitInit(args);
			}else if(args[1].equals("rm")){
				jitRM(args);
			}else if(args[1].equals("add")){
				jitAdd(args);
			}else if(args[1].equals("log")){
				jitLog(args);
			}else if(args[1].equals("reset")){
				jitReset(args);
			}else if(args[1].equals("commit")){
				jitCommit(args);
			}else if(args[1].equals("status")){
				jitStatus(args);
			}else if(args[1].equals("branch")) {
				jitBranch(args);
			}else if(args[1].equals("checkout")) {
				jitCheckout(args);
			}else if(args[1].equals("merge")){
				jitMerge(args);
			}else if(args[1].equals("diff")){
				jitDiff(args);
			}else if(args[1].equals("cat-file")){
				jitCatFile(args);
			}else if(args[1].equals("ls-files")){
				jitListIndex(args);
			}
			else {
				System.out.println("jit: " + args[1] + " is not a git command. See 'git help'.");
			}
		}
	}




}


